/*! 
 * Header file for a collision heightfield.
 */
#ifndef PHXCOLLISIONTERRAIN_H_INCLUDE_GUARD
#define PHXCOLLISIONTERRAIN_H_INCLUDE_GUARD

#include "PhxCore.h"
#include "PhxCollisionPrimitive.h"
#include "PhxCollisionPrimitiveDescriptor.h"
#include "PhxMaterial.h"

#include <vector>

namespace phx
{
	/*!
     * A single heightfield vertex.
     */
	struct HeightFieldVertex
	{
		/*! 
		 * Height of the vertex
		 */
		real mVertexHeight;

		/*! 
		 * Material of the vertex
		 */
		Material mVertexMaterial;

		// TODO: Add a coefficient of destruction here!
	};

	typedef std::vector<HeightFieldVertex> HeightFieldData;

	/*!
	 * Heightfield.
	 *
	 * \note An heightfield is a numeric heightmap.
	 */
	class HeightField
	{
	private:

		size_t mRows;
		size_t mColumns;

		HeightFieldData mElements;

	public:

		HeightField() : mRows(0), mColumns(0) { }

		HeightField(const size_t iRows, const size_t iColumns) 
			: mRows(iRows), mColumns(iColumns) { }

		HeightField(const size_t iRows, const size_t iColumns, const HeightFieldData& iElements) 
			: mRows(iRows), mColumns(iColumns), mElements(iElements) { }

		const HeightFieldVertex& GetVertex(const size_t, const size_t) const;
		HeightFieldVertex& GetVertex(const size_t, const size_t);
		const HeightFieldData& GetVertexes() const;
		HeightFieldData& GetVertexes();

		const real GetHeight(const size_t, const size_t) const;
		const Material& GetMaterial(const size_t, const size_t) const;
		Material& GetMaterial(const size_t, const size_t);

		void SetVertex(const size_t, const size_t, const HeightFieldVertex&);
		void SetVertexes(const HeightFieldData&);

		void SetHeight(const size_t, const size_t, const real);
		void SetMaterial(const size_t, const size_t, const Material&);

		size_t GetRows() const;
		size_t GetColumns() const;
	};	
	 
	/*!
	 * Terrain descriptor.
	 */
	struct CollisionTerrainDescriptor : public CollisionPrimitiveDescriptor
	{
		/*!
		 * Constructor.
		 */
		CollisionTerrainDescriptor() 
			: mHorizontalScaleFactor(0), mVerticalScaleFactor(0) { }

		/*!
		 * Destructor (virtual).
		 */
		~CollisionTerrainDescriptor() { }

		/*!
		 * Return the shape type.
		 */
		virtual ShapeTypes GetShapeType() const { return TERRAIN_SHAPE; }

		/*!
		 * Terrain heightfield.
		 */
		HeightField mHeights;

		/*! 
		 * XY scale factor.
		 */
		real mHorizontalScaleFactor;
		/*!
		 * Z scale factor.
		 */
		real mVerticalScaleFactor;
	};

	/*!
	 * Terrain collision geometry.
     */
	class CollisionTerrain : public CollisionPrimitive
	{
	protected:

		/*!
		 * Terrain heightfield.
		 */
		HeightField mHeights;

		/*! 
		 * XY scale factor.
		 */
		real mHorizontalScaleFactor;
		/*!
		 * Z scale factor.
		 */
		real mVerticalScaleFactor;

		/*! 
		 * Terrain needs to be redrawn?.
		 */
		bool mModified;

	public:

		/*!
		 * Constructor.
		 */
		CollisionTerrain();

		/*!
		 * Constructor with descriptor.
		 */
		CollisionTerrain(const CollisionTerrainDescriptor& desc);
		
		/*!
		 * Destructor (virtual).
		 */
		virtual ~CollisionTerrain() { };

		/*!
		 * Return the shape type.
		 */
		virtual inline ShapeTypes GetShapeType() const { return TERRAIN_SHAPE; }

		/*!
		 * Set terrain's heightfield.
		 */
		void SetHeightField(const HeightField&);
		/*!
		 * Set terrain's heightfield.
		 */
		void SetHeightFieldData(size_t, size_t, const HeightFieldData&);
		/*!
		 * Get terrain's heightfield.
		 */
		const HeightField& GetHeightField() const;
		/*!
		 * Get terrain's heightfield.
		 */
		HeightField& GetHeightField();
		/*!
		 * Get terrain's heightfield data (vertexes data).
		 */
		const HeightFieldData& GetHeightFieldData() const;
		/*!
		 * Get terrain's heightfield data (vertexes data).
		 */
		HeightFieldData& GetHeightFieldData();

		/*!
		 * Return the number of rows of the terrain's heightfield.
		 */
		size_t GetRows() const;
		/*!
		 * Return the number of columns of the terrain's heightfield.
		 */
		size_t GetColumns() const;

		/*!
		 * Set the horizontal scale factor applied to the heightmap.
		 */
		void SetHorizontalScaleFactor(const real);
		/*!
		 * Get the horizontal scale factor applied to the heightmap.
		 */
		const real GetHorizontalScaleFactor() const;

		/*!
		 * Set the vertical scale factor applied to the heightmap.
		 */
		void SetVerticalScaleFactor(const real);
		/*!
		 * Get the vertical scale factor applied to the heightmap.
		 */
		const real GetVerticalScaleFactor() const;

		/*!
		 * Set horizontal and vertical scale factor applied to the heightmap.
		 */
		void SetScale(const real, const real);

		/*!
		 * Return the scaled height of a specified vertex.
		 */
		const real GetHeight(const size_t, const size_t) const;

		/*!
		 * Return the material of a specified vertex.
		 */
		const Material& GetMaterial(const size_t, const size_t) const;

		/*!
		 * Return true if the terrain has been modified and it needs to be redrawn.
		 */
		bool HasBeenModified() const;

		/*!
		 * Set terrain's modification status.
		 */
		void SetModificationStatus(const bool iStatus = false);

		/*!
		 * Modify the terrain simulating an explosion.
		 */ 
		void ModifyTerrain(const Vector3& iExplosionPosition, const real iRay, const real iDepth);
	};
}
#endif